<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>未来之窗 - 文章向量搜索</title>
    <style>
        :root {
            --primary: #3a7bd5;
            --secondary: #6d8cb5;
            --light: #f5f7fa;
            --dark: #2c3e50;
            --success: #2ecc71;
            --warning: #e67e22;
            --danger: #e74c3c;
        }
        
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Microsoft YaHei', sans-serif;
        }
        
        body {
            background-color: var(--light);
            color: var(--dark);
            line-height: 1.6;
            padding: 20px;
        }
        
        header {
            text-align: center;
            padding: 30px 0;
            background: linear-gradient(135deg, var(--primary), var(--secondary));
            color: white;
            border-radius: 8px;
            margin-bottom: 30px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        
        h1 {
            font-size: 2.2rem;
            margin-bottom: 10px;
        }
        
        .subtitle {
            font-size: 1.1rem;
            opacity: 0.9;
            max-width: 700px;
            margin: 0 auto;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 350px 1fr;
            gap: 30px;
        }
        
        @media (max-width: 900px) {
            .container {
                grid-template-columns: 1fr;
            }
        }
        
        .card {
            background: white;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
            margin-bottom: 30px;
        }
        
        .card h2 {
            color: var(--primary);
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 2px solid #f1f1f1;
        }
        
        .search-group {
            margin-bottom: 20px;
        }
        
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: 500;
        }
        
        input, textarea, select {
            width: 100%;
            padding: 10px 12px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 1rem;
            margin-bottom: 15px;
        }
        
        textarea {
            min-height: 100px;
            resize: vertical;
        }
        
        button {
            background-color: var(--primary);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 1rem;
            transition: background-color 0.2s;
        }
        
        button:hover {
            background-color: #2d62b3;
        }
        
        button.secondary {
            background-color: var(--secondary);
        }
        
        button.success {
            background-color: var(--success);
        }
        
        .button-group {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }
        
        .results-section {
            margin-top: 20px;
        }
        
        .result-item {
            border: 1px solid #eee;
            border-radius: 6px;
            padding: 15px;
            margin-bottom: 15px;
            transition: transform 0.2s, box-shadow 0.2s;
        }
        
        .result-item:hover {
            transform: translateY(-2px);
            box-shadow: 0 3px 10px rgba(0,0,0,0.08);
        }
        
        .result-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        
        .result-title {
            font-weight: bold;
            color: var(--primary);
            font-size: 1.1rem;
        }
        
        .similarity-score {
            padding: 4px 10px;
            border-radius: 20px;
            font-size: 0.9rem;
            font-weight: bold;
        }
        
        .high {
            background-color: rgba(46, 204, 113, 0.15);
            color: var(--success);
        }
        
        .medium {
            background-color: rgba(230, 126, 34, 0.15);
            color: var(--warning);
        }
        
        .low {
            background-color: rgba(231, 76, 60, 0.15);
            color: var(--danger);
        }
        
        .result-content {
            color: #555;
            font-size: 0.95rem;
            margin-bottom: 10px;
            display: -webkit-box;
            -webkit-line-clamp: 3;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }
        
        .vocab-list {
            max-height: 200px;
            overflow-y: auto;
            border: 1px solid #eee;
            border-radius: 4px;
            margin-top: 15px;
        }
        
        .vocab-item {
            padding: 8px 12px;
            border-bottom: 1px solid #f5f5f5;
            display: flex;
            justify-content: space-between;
        }
        
        .vocab-item:last-child {
            border-bottom: none;
        }
        
        .status-bar {
            font-size: 0.9rem;
            color: #777;
            margin-top: 15px;
            padding-top: 10px;
            border-top: 1px dashed #eee;
        }
        
        .article-meta {
            font-size: 0.85rem;
            color: #888;
        }
    </style>
</head>
<body>
    <header>
        <h1>未来之窗 - 文章向量搜索</h1>
        <p class="subtitle">基于向量余弦相似度的智能文章内容搜索工具，快速找到最相关的文章</p>
    </header>
    
    <div class="container">
        <div class="sidebar">
            <div class="card">
                <h2>搜索设置</h2>
                <div class="search-group">
                    <label for="searchQuery">搜索关键词或句子</label>
                    <textarea id="searchQuery" placeholder="输入你想搜索的内容...例如：人工智能的发展趋势"></textarea>
                </div>
                
                <div class="search-group">
                    <label for="resultCount">结果数量</label>
                    <select id="resultCount">
                        <option value="5">5条</option>
                        <option value="10" selected>10条</option>
                        <option value="15">15条</option>
                        <option value="20">20条</option>
                    </select>
                </div>
                
                <div class="button-group">
                    <button id="searchBtn">开始搜索</button>
                    <button id="clearBtn" class="secondary">清空</button>
                </div>
                
                <div class="status-bar">
                    <div>当前文章库: <span id="articleCount">0</span> 篇</div>
                    <div>词汇表大小: <span id="vocabCount">0</span> 个</div>
                </div>
            </div>
            
            <div class="card">
                <h2>词汇管理</h2>
                <div class="search-group">
                    <label for="wordInput">添加关键词</label>
                    <input type="text" id="wordInput" placeholder="输入要添加的关键词...">
                    <button id="addWordBtn" class="success">添加词汇</button>
                </div>
                
                <div>
                    <h3>常用词汇</h3>
                    <div class="vocab-list" id="vocabList"></div>
                </div>
            </div>
        </div>
        
        <div class="main-content">
            <div class="card">
                <h2>搜索结果</h2>
                <div id="searchStatus" style="text-align:center; padding:30px 0; color:#777;">
                    请输入搜索内容并点击"开始搜索"
                </div>
                <div class="results-section" id="resultsContainer"></div>
            </div>
            
            <div class="card">
                <h2>文章库管理</h2>
                <div class="search-group">
                    <label for="articleTitle">文章标题</label>
                    <input type="text" id="articleTitle" placeholder="输入文章标题">
                    
                    <label for="articleContent">文章内容</label>
                    <textarea id="articleContent" placeholder="输入文章内容"></textarea>
                    
                    <label for="articleVector">文章向量 (JSON数组)</label>
                    <textarea id="articleVector" placeholder="[0.12, 0.34, 0.56, ...]"></textarea>
                </div>
                
                <div class="button-group">
                    <button id="addArticleBtn" class="success">添加文章</button>
                    <button id="exportBtn" class="secondary">导出文章库</button>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 词汇表模块
        const Vocabulary = (() => {
            class Vocabulary {
                constructor() {
                    this.words = new Map(); // 词 -> 索引
                    this.index = 0;
                }
                
                // 添加词汇
                addWord(word) {
                    const processed = word.trim().toLowerCase();
                    if (!processed || this.words.has(processed)) return false;
                    
                    this.words.set(processed, this.index++);
                    return true;
                }
                
                // 获取词汇索引
                getWordIndex(word) {
                    return this.words.get(word.trim().toLowerCase()) ?? null;
                }
                
                // 获取词汇数量
                getSize() {
                    return this.index;
                }
                
                // 获取所有词汇
                getAllWords() {
                    return Array.from(this.words.keys());
                }
                
                // 导出为JSON
                toJSON() {
                    return {
                        words: Object.fromEntries(this.words),
                        index: this.index
                    };
                }
                
                // 从JSON导入
                fromJSON(data) {
                    this.words = new Map(Object.entries(data.words));
                    this.index = data.index;
                }
            }
            
            return Vocabulary;
        })();
        
        // 向量计算模块
        const VectorMath = (() => {
            class VectorMath {
                // 计算点积
                static dotProduct(vec1, vec2) {
                    if (vec1.length !== vec2.length) throw new Error("向量长度不匹配");
                    
                    let product = 0;
                    for (let i = 0; i < vec1.length; i++) {
                        product += vec1[i] * vec2[i];
                    }
                    return product;
                }
                
                // 计算向量模长
                static magnitude(vec) {
                    let sum = 0;
                    for (let i = 0; i < vec.length; i++) {
                        sum += vec[i] **2;
                    }
                    return Math.sqrt(sum);
                }
                
                // 计算余弦相似度
                static cosineSimilarity(vec1, vec2) {
                    const dot = this.dotProduct(vec1, vec2);
                    const mag1 = this.magnitude(vec1);
                    const mag2 = this.magnitude(vec2);
                    
                    if (mag1 === 0 || mag2 === 0) return 0;
                    
                    return parseFloat((dot / (mag1 * mag2)).toFixed(6));
                }
            }
            
            return VectorMath;
        })();
        
        // 文章库模块
        const ArticleStore = (() => {
            class ArticleStore {
                constructor() {
                    this.articles = []; // {id, title, content, vector}
                    this.nextId = 1;
                }
                
                // 添加文章
                addArticle(title, content, vector) {
                    if (!title || !vector || !Array.isArray(vector)) return null;
                    
                    const id = `art-${this.nextId++}`;
                    this.articles.push({
                        id,
                        title,
                        content: content || "",
                        vector: vector.map(v => parseFloat(v.toFixed(6)))
                    });
                    
                    return id;
                }
                
                // 获取所有文章
                getAllArticles() {
                    return [...this.articles];
                }
                
                // 根据ID获取文章
                getArticleById(id) {
                    return this.articles.find(a => a.id === id) || null;
                }
                
                // 搜索相似文章
                searchSimilar(vector, topN = 10) {
                    const results = this.articles.map(article => ({
                        article,
                        similarity: VectorMath.cosineSimilarity(vector, article.vector)
                    }));
                    
                    // 按相似度排序并返回前N条
                    return results.sort((a, b) => b.similarity - a.similarity)
                                  .slice(0, topN);
                }
                
                // 获取文章数量
                getCount() {
                    return this.articles.length;
                }
                
                // 导出为JSON
                toJSON() {
                    return this.articles;
                }
                
                // 从JSON导入
                fromJSON(data) {
                    this.articles = data;
                    this.nextId = Math.max(...this.articles.map(a => parseInt(a.id.split('-')[1])), 0) + 1;
                }
            }
            
            return ArticleStore;
        })();
        
        // 生成演示数据
        function generateDemoData() {
            // 主题分类
            const topics = [
                { name: "人工智能", keywords: ["人工智能", "机器学习", "深度学习", "神经网络", "算法"] },
                { name: "量子计算", keywords: ["量子", "量子计算", "量子比特", "叠加态", "纠缠"] },
                { name: "生物医药", keywords: ["生物", "医学", "基因", "药物", "治疗"] },
                { name: "新能源", keywords: ["能源", "电池", "光伏", "可再生", "环保"] },
                { name: "太空探索", keywords: ["太空", "宇宙", "航天", "星球", "探测器"] },
                { name: "区块链", keywords: ["区块链", "加密", "数字货币", "分布式", "账本"] }
            ];
            
            // 生成演示词汇表
            const vocab = new Vocabulary();
            topics.forEach(topic => {
                topic.keywords.forEach(word => vocab.addWord(word));
            });
            
            // 生成30篇演示文章
            const articles = [];
            const vectorSize = vocab.getSize();
            
            for (let i = 0; i < 30; i++) {
                // 随机选择主题
                const topic = topics[Math.floor(Math.random() * topics.length)];
                const topicIndex = topics.indexOf(topic);
                
                // 生成向量 (主题相关词权重更高)
                const vector = [];
                for (let j = 0; j < vectorSize; j++) {
                    // 主题相关词权重更高
                    let weight = Math.random() * 0.5;
                    
                    // 为当前主题的关键词增加权重
                    if (j < topic.keywords.length && topicIndex === Math.floor(j / (vectorSize / topics.length))) {
                        weight += 0.5 + Math.random() * 0.5;
                    }
                    
                    vector.push(parseFloat(weight.toFixed(4)));
                }
                
                // 生成文章内容
                const content = `${topic.name}技术的最新发展。${topic.keywords[0]}在${topic.keywords[1]}领域的应用取得了突破，研究人员利用${topic.keywords[2]}方法实现了${Math.floor(Math.random() * 100)}%的性能提升。这一进展将对${topic.keywords[3]}行业产生深远影响，预计在未来${Math.floor(Math.random() * 5) + 1}年内得到广泛应用。相关专家表示，这一技术突破将推动整个行业的变革，为${topic.keywords[4]}领域带来新的发展机遇。`;
                
                articles.push({
                    title: `${topic.name}研究进展 (演示文章${i+1})`,
                    content: content,
                    vector: vector
                });
            }
            
            return { vocab, articles };
        }
        
        // 页面交互逻辑 - 包含所有按钮事件处理
        document.addEventListener('DOMContentLoaded', () => {
            // 初始化模块
            const vocabulary = new Vocabulary();
            const articleStore = new ArticleStore();
            
            // 加载演示数据
            const demoData = generateDemoData();
            vocabulary.fromJSON(demoData.vocab.toJSON());
            demoData.articles.forEach(art => {
                articleStore.addArticle(art.title, art.content, art.vector);
            });
            
            // 获取DOM元素
            const searchBtn = document.getElementById('searchBtn');
            const addWordBtn = document.getElementById('addWordBtn');
            const addArticleBtn = document.getElementById('addArticleBtn');
            const exportBtn = document.getElementById('exportBtn');
            const clearBtn = document.getElementById('clearBtn');
            
            // 其他DOM元素
            const searchQuery = document.getElementById('searchQuery');
            const resultCount = document.getElementById('resultCount');
            const resultsContainer = document.getElementById('resultsContainer');
            const searchStatus = document.getElementById('searchStatus');
            const articleCount = document.getElementById('articleCount');
            const vocabCount = document.getElementById('vocabCount');
            const wordInput = document.getElementById('wordInput');
            const vocabList = document.getElementById('vocabList');
            const articleTitle = document.getElementById('articleTitle');
            const articleContent = document.getElementById('articleContent');
            const articleVector = document.getElementById('articleVector');
            
            // 更新状态信息
            function updateStatus() {
                articleCount.textContent = articleStore.getCount();
                vocabCount.textContent = vocabulary.getSize();
            }
            
            // 更新词汇表显示
            function updateVocabDisplay() {
                vocabList.innerHTML = '';
                const words = vocabulary.getAllWords().slice(0, 10); // 显示前10个
                
                words.forEach(word => {
                    const item = document.createElement('div');
                    item.className = 'vocab-item';
                    item.innerHTML = `
                        <span>${word}</span>
                        <span style="color:#999">${vocabulary.getWordIndex(word)}</span>
                    `;
                    vocabList.appendChild(item);
                });
                
                if (vocabulary.getSize() > 10) {
                    const more = document.createElement('div');
                    more.className = 'vocab-item';
                    more.style.textAlign = 'center';
                    more.style.color = '#777';
                    more.textContent = `... 还有 ${vocabulary.getSize() - 10} 个词汇`;
                    vocabList.appendChild(more);
                }
            }
            
            // 格式化相似度分数显示
            function formatSimilarity(score) {
                let className = 'low';
                if (score >= 0.7) className = 'high';
                else if (score >= 0.3) className = 'medium';
                
                return {
                    text: score.toFixed(4),
                    class: className,
                    label: className === 'high' ? '高度相似' : 
                           className === 'medium' ? '中度相似' : '低相似度'
                };
            }
            
            // searchBtn 事件处理 - 执行搜索
            searchBtn.addEventListener('click', () => {
                const query = searchQuery.value.trim();
                if (!query) {
                    alert('请输入搜索内容');
                    return;
                }
                
                // 基于搜索词生成向量
                const words = query.split(/\s+/);
                const vectorSize = Math.max(vocabulary.getSize(), 10);
                const searchVector = new Array(vectorSize).fill(0);
                
                // 根据搜索词中的关键词调整向量
                words.forEach(word => {
                    const index = vocabulary.getWordIndex(word);
                    if (index !== null && index < vectorSize) {
                        searchVector[index] = 0.8 + Math.random() * 0.2;
                    }
                });
                
                // 如果没有匹配的关键词，随机生成一些值
                if (searchVector.every(v => v === 0)) {
                    for (let i = 0; i < vectorSize; i++) {
                        searchVector[i] = Math.random() * 0.6;
                    }
                }
                
                // 搜索相似文章
                const topN = parseInt(resultCount.value);
                const results = articleStore.searchSimilar(searchVector, topN);
                
                // 显示结果
                searchStatus.style.display = 'none';
                resultsContainer.innerHTML = '';
                
                if (results.length === 0) {
                    resultsContainer.innerHTML = '<div style="text-align:center; padding:30px 0;">没有找到匹配的文章</div>';
                    return;
                }
                
                results.forEach(item => {
                    const sim = formatSimilarity(item.similarity);
                    const resultItem = document.createElement('div');
                    resultItem.className = 'result-item';
                    
                    resultItem.innerHTML = `
                        <div class="result-header">
                            <div class="result-title">${item.article.title}</div>
                            <div class="similarity-score ${sim.class}">
                                ${sim.text} (${sim.label})
                            </div>
                        </div>
                        <div class="result-content">${item.article.content}</div>
                        <div class="article-meta">向量维度: ${item.article.vector.length}</div>
                    `;
                    
                    resultsContainer.appendChild(resultItem);
                });
            });
            
            // addWordBtn 事件处理 - 添加词汇
            addWordBtn.addEventListener('click', () => {
                const word = wordInput.value.trim();
                if (!word) {
                    alert('请输入要添加的词汇');
                    return;
                }
                
                const added = vocabulary.addWord(word);
                if (added) {
                    wordInput.value = '';
                    updateVocabDisplay();
                    updateStatus();
                    alert(`词汇"${word}"已添加到词汇表`);
                } else {
                    alert('该词汇已存在或无效');
                }
            });
            
            // addArticleBtn 事件处理 - 添加文章
            addArticleBtn.addEventListener('click', () => {
                const title = articleTitle.value.trim();
                const content = articleContent.value.trim();
                let vector;
                
                try {
                    vector = JSON.parse(articleVector.value.trim());
                    if (!Array.isArray(vector)) throw new Error();
                } catch (e) {
                    alert('请输入有效的向量数据(JSON数组)');
                    return;
                }
                
                if (!title) {
                    alert('请输入文章标题');
                    return;
                }
                
                const id = articleStore.addArticle(title, content, vector);
                if (id) {
                    articleTitle.value = '';
                    articleContent.value = '';
                    articleVector.value = '';
                    updateStatus();
                    alert('文章添加成功');
                } else {
                    alert('文章添加失败');
                }
            });
            
            // exportBtn 事件处理 - 导出文章库
            exportBtn.addEventListener('click', () => {
                const data = {
                    articles: articleStore.toJSON(),
                    vocabulary: vocabulary.toJSON()
                };
                
                const jsonStr = JSON.stringify(data, null, 2);
                const blob = new Blob([jsonStr], { type: 'application/json' });
                const url = URL.createObjectURL(blob);
                
                const a = document.createElement('a');
                a.href = url;
                a.download = 'article_vector_data_' + new Date().getTime() + '.json';
                document.body.appendChild(a);
                a.click();
                
                setTimeout(() => {
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                }, 0);
            });
            
            // clearBtn 事件处理 - 清空搜索
            clearBtn.addEventListener('click', () => {
                searchQuery.value = '';
                resultsContainer.innerHTML = '';
                searchStatus.style.display = 'block';
            });
            
            // 初始化界面
            updateStatus();
            updateVocabDisplay();
        });
    </script>
</body>
</html>
    